package com.ydl.iec.transport;

import com.ydl.iec.util.ByteUtil;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class FileFrameFactory {
    private static final FileFrameFactory factory = new FileFrameFactory();
    public static FileFrame decodeToFileFrame(byte[] data){
        return factory.buildFileFrameFromData(data);
    }
    public static List<byte[]> encodeFromFileFrame(FileFrame fileFrame){
        return factory.parseFileFrame(fileFrame);
    }
    private FileFrame buildFileFrameFromData(byte[] data){
        FileFrame frame = new FileFrame();
        int index = 1;
        frame.setApduLen(data[index++]);
        index += setControl(frame,data,index);
        frame.setType(data[index++]);
        frame.setVsq(data[index++]);
        index += setCot(frame,data,index);
        index += setAddress(frame,data,index);
        index += setRegister(frame,data,index);
        byte attachType = data[index++];
        frame.setAttachType(attachType);
        byte op = data[index++];
        frame.setOp(op);
        AttachTypeEnum type = AttachTypeEnum.valOf(op);
        switch (type){
            case DIRCALL: //目录召唤
                frame.setAttach(buildDirCallAttachData(data,index));
                break;
            case FILECALL: //召唤文件激活
                frame.setAttach(buildFileCallAttachData(data,index));
                break;
            case FILETRANCFM:
                frame.setAttach(buildTransCallAttachData(data,index));
                break;
            default:
                break;
        }

        return frame;
    }

    private int setControl(FileFrame frame,byte[] data,int beginIndex){
        short rxCount = 0;
        short txCount = 0;

        txCount = data[beginIndex + 1]; //发送高位
        txCount = (short)(txCount << 8 | data[beginIndex]); //发送低位

        rxCount = data[beginIndex + 3]; //发送高位
        rxCount = (short)(rxCount << 8 | data[beginIndex + 2]); //发送低位

        frame.setRxCount(rxCount);
        frame.setTxCount(txCount);

        return 4;
    }

    private int setCot(FileFrame frame,byte[] data,int beginIndex){
        int tmp = data[beginIndex + 1];
        tmp = tmp<<8 | data[beginIndex];
        frame.setCot((short)tmp);
        return 2;
    }

    private int setAddress(FileFrame frame,byte[] data,int beginIndex){
        int tmp = data[beginIndex + 1];
        tmp = tmp<<8 | data[beginIndex];
        frame.setAddress((short)tmp);
        return 2;
    }

    private int setRegister(FileFrame frame,byte[] data,int beginIndex){
        int tmp = data[beginIndex + 2];
        tmp = tmp << 8 | data[beginIndex + 1];
        tmp = tmp << 8 | data[beginIndex];
        frame.setRegister(tmp);
        return 3;
    }

    private AttachDataCallDir buildDirCallAttachData(byte[] data,int beginIndex){
        AttachDataCallDir dataObject = new AttachDataCallDir();
        int index = beginIndex;
        dataObject.setId((int)buildNumber(data,index,4));
        index += 4;
        dataObject.setName(buildFileName(data,index));
        index += data[index] + 1; // 目录长度加1
        dataObject.setFlag(data[index++]);

        int tmpLocal = index + 7;
        dataObject.setStartTime(ByteUtil.byte2Hdate(Arrays.copyOfRange(data,index,tmpLocal)));

        index = tmpLocal;
        tmpLocal = index + 7;
        dataObject.setEndTime(ByteUtil.byte2Hdate(Arrays.copyOfRange(data,index,tmpLocal)));
        return dataObject;
    }

    private AttachDataCallFile buildFileCallAttachData(byte[] data,int beginIndex){
        AttachDataCallFile file = new AttachDataCallFile();
        file.setFileNameLen(data[beginIndex]);
        file.setFileName(buildFileName(data,beginIndex));
        return file;
    }

    private AttachDataCallTrans buildTransCallAttachData(byte[] data,int beginIndex){
        AttachDataCallTrans trans = new AttachDataCallTrans();
        int index = beginIndex;
        trans.setId((int)buildNumber(data,index,4));
        index += 4;
        trans.setSegment((int)buildNumber(data,index,4));
        index += 4;
        trans.setMore(data[index]);
        return trans;
    }

    private long buildNumber(byte[] val,int beginIndex,int len){
        long ret = val[beginIndex + len - 1];
        for(int i = beginIndex + len - 2; i >=beginIndex; i--){
            ret = ret << 8 | (val[i]&0xff);
        }
        return ret;
    }

    private String buildFileName(byte[] val,int beginIndex){
        int size = val[beginIndex];
        String dirName = null;
        if(size > 0){
            dirName= new String(val,beginIndex + 1,size, StandardCharsets.UTF_8);
        }
        return dirName;
    }

    public List<byte[]> parseFileFrame(FileFrame fileFrame){
        byte[] result = new byte[128];
        int index = 0;
        result[index++] = fileFrame.getFLAG();
        result[index++] = 0;
        index += getControl(fileFrame,result,index);
        result[index++] = fileFrame.getType();
        result[index++] = fileFrame.getVsq();
        index += getCot(fileFrame,result,index);
        index += getAddress(fileFrame,result,index);
        index += getRegister(fileFrame,result,index);
        result[index++] = fileFrame.getAttachType();
        byte op = fileFrame.getOp(); //操作标识
        result[index++] = op;
        AttachTypeEnum type = AttachTypeEnum.valOf(op);
        List<byte[]> lstFileData = null;
        switch (type){
            case DIRCFM: //目录召唤确认
                AttachDataReplyDir attachDir = (AttachDataReplyDir)fileFrame.getAttach();
                lstFileData = parseDirData(attachDir,index);
                break;
            case FILECFM: //读文件激活确认
                AttachDataReplyFile attachFile = (AttachDataReplyFile)fileFrame.getAttach();
                lstFileData = parseFileData(attachFile,index);
                break;
            case FILETRAN: //读文件数据传输
                AttachDataReplyTrans transFile = (AttachDataReplyTrans)fileFrame.getAttach();
                lstFileData = parseTransData(transFile,index);
                break;
            default:
                lstFileData = new ArrayList<>();
                break;
        }
        List<byte[]> lstResult = new ArrayList<>(10);
        for (byte[] data : lstFileData) {
            result[1] = (byte)(index + data.length - 2);
            copyDataToArray(result,data,index,data.length,lstResult);
        }
        return lstResult;
    }

    private int getControl(FileFrame frame,byte[] data,int beginIndex){
        short rxCount = frame.getRxCount();
        short txCount = frame.getTxCount();
        data[beginIndex] = (byte)(txCount & 0xFE);
        data[beginIndex + 1] = (byte)((txCount >> 8) & 0xFF);
        data[beginIndex + 2] = (byte)(rxCount & 0xFE);
        data[beginIndex + 3] = (byte)((rxCount >> 8) & 0xFF);
        return 4;
    }
    private int getCot(FileFrame frame,byte[] data,int beginIndex){
        short cot = frame.getCot();
        data[beginIndex] = (byte)cot;
        data[beginIndex + 1] = (byte)(cot >> 8);
        return 2;
    }
    private int getAddress(FileFrame frame,byte[] data,int beginIndex){
        int tmp = frame.getAddress();
        data[beginIndex] = (byte)tmp;
        data[beginIndex + 1] = (byte)(tmp >> 8);
        return 2;
    }
    private int getRegister(FileFrame frame,byte[] data,int beginIndex){
        int register = frame.getRegister();
        data[beginIndex] = (byte)register;
        data[beginIndex + 1] = (byte)(register >> 8);
        data[beginIndex + 2] = (byte)(register >> 16);
        return 3;
    }

    private List<byte[]> parseDirData(AttachDataReplyDir attach,final int beginIndex){
        List<byte[]> lstByteFiles = new ArrayList<>();
        byte[] remainder = new byte[255 - beginIndex];
        int success = attach.getSuccess();
        int remainIndex = 0;
        remainder[remainIndex++] = (byte)success;
        int id = attach.getId();
        for(int i = 0; i < 4; i++){
            remainder[remainIndex++] = (byte)(id >> (8 * i));
        }
        if(success == 1){ //失败：如果失败，直接返回
            remainder[remainIndex++] = 0; //无后续
            lstByteFiles.add(Arrays.copyOf(remainder,remainIndex));
        }else{
            List<AttachFile> lstFiles = attach.getAttachFiles();
            if(lstFiles.isEmpty()){
                remainder[remainIndex++] = 0; //无后续
                lstByteFiles.add(Arrays.copyOf(remainder,remainIndex));
            }else{
                int size = remainder.length - remainIndex;
                byte[] fileInfos = new byte[size];
                int fileCount = 0;
                int fileIterate = 0;
                int fileAmount = lstFiles.size();
                int idx = 2;
                boolean isFinished = false;
                for(AttachFile af : lstFiles){
                    fileIterate++;
                    String fileName = af.getFileName();
                    byte[] fileNameArray = fileName.getBytes(StandardCharsets.UTF_8);
                    if((idx + 15 + fileNameArray.length) >= size){
                        fileInfos[0] = 1;
                        fileInfos[1] = (byte)fileCount;
                        copyDataToArray(remainder,fileInfos,remainIndex,idx,lstByteFiles);
                        fileCount = 0;
                        idx = 2;
                        if(fileIterate < fileAmount){
                            isFinished = false;
                            fileCount++;
                            idx = singleFileProcess(fileInfos,fileNameArray,af,idx);
                        }else{
                            fileInfos[0] = 0;
                            isFinished = true;
                        }
                    }else{
                        isFinished = false;
                        fileCount++;
                        idx = singleFileProcess(fileInfos,fileNameArray,af,idx);
                    }
                }
                if(!isFinished){
                    fileInfos[0] = 0;
                    fileInfos[1] = (byte)fileCount;
                    copyDataToArray(remainder,fileInfos,remainIndex,idx,lstByteFiles);
                }
            }
        }
        return lstByteFiles;
    }

    private List<byte[]> parseFileData(AttachDataReplyFile attach,final int beginIndex){
        List<byte[]> lstByteFiles = new ArrayList<>();
        byte[] remainder = new byte[156 - beginIndex]; //最大文件名称长度决定
        int success = attach.getSuccess();
        int remainIndex = 0;
        remainder[remainIndex++] = (byte)success;

        if(success == 0){
            String fileName = attach.getFileName();
            byte[] fileNameArray = fileName.getBytes(StandardCharsets.UTF_8);
            remainder[remainIndex++] = (byte)fileNameArray.length;
            for(byte b : fileNameArray){
                remainder[remainIndex++] = b;
            }
        }else{
            remainder[remainIndex++] = 0;
        }
        int id = attach.getId();
        for(int i = 0; i < 4; i++){
            remainder[remainIndex++] = (byte)(id >> (8 * i));
        }
        int fileSize = attach.getSize();
        for(int i = 0; i < 4; i++){
            remainder[remainIndex++] = (byte)(fileSize>>(8*i));
        }
        lstByteFiles.add(Arrays.copyOf(remainder,remainIndex));
        return lstByteFiles;
    }

    private  List<byte[]> parseTransData(AttachDataReplyTrans transFile,int beginIndex) {
        List<byte[]> lstByteFiles = new ArrayList<>();
        byte[] remainder = new byte[255 - beginIndex]; //最大文件名称长度决定
        int remainIndex = 0;
        int id = transFile.getId();
        for(int i = 0; i < 4; i++){
            remainder[remainIndex++] = (byte)(id >> (8 * i));
        }
        File f = transFile.getTransFile();
        int segment = transFile.getSegment();
        for(int i = 0; i < 4; i++){
            remainder[remainIndex++] = (byte)(segment >> (8 * i));
        }
        if(f == null){
            remainder[remainIndex++] = 0; //无后续
            remainder[remainIndex++] = 0; //校验数据 0
            lstByteFiles.add(Arrays.copyOf(remainder,remainIndex));
            return lstByteFiles;
        }

        int size = (int)f.length();
        if(segment >= size){ //没有内容了
            remainder[remainIndex++] = 0; //无后续
            remainder[remainIndex++] = 0; //校验数据 0
            lstByteFiles.add(Arrays.copyOf(remainder,remainIndex));
        }else{ //需要继续读文件
            try(RandomAccessFile rafile = new RandomAccessFile(f,"r");
            ){
                final int pubSize = 10; //公共字节数量
                byte[] data = new byte[remainder.length - pubSize];
                rafile.seek(segment);
                int len = rafile.read(data,0,data.length);
                int total = segment + len;
                if(len > 0){//回复文件内容
                    if(size > total){//有后续
                        remainder[remainIndex++] = 1;
                    }else{//没有后续
                        remainder[remainIndex++] = 0;
                    }
                    int mod = 0;
                    for(int i = 0; i < len; i++){
                        remainder[remainIndex++] = data[i];
                        mod = mod +data[i];
                    }
                    remainder[remainIndex++] = (byte)mod;
                    lstByteFiles.add(Arrays.copyOf(remainder,remainIndex));
                }else{//回复没有文件内容了
                    remainder[remainIndex++] = 0; //无后续
                    remainder[remainIndex++] = 0; //校验数据 0
                    lstByteFiles.add(Arrays.copyOf(remainder,remainIndex));
                }
            }catch (IOException e){
                e.printStackTrace();
            }

        }

//        try(RandomAccessFile rafile = new RandomAccessFile(f,"r");
//        ){
//            final int pubSize = 10; //公共字节数量
//            byte[] data = new byte[remainder.length - pubSize];
////            rafile.seek(transFile.getOffset());
//            int len = rafile.read(data,segment,data.length);
//            int total = segment + len;
//            if(len > 0){//回复文件内容
//                for(int i = 0; i < 4; i++){
//                    remainder[remainIndex++] = (byte)(segment >> (8 * i));
//                }
//                if(size > total){//有后续
//                    remainder[remainIndex++] = 1;
//                }else{//没有后续
//                    remainder[remainIndex++] = 0;
//                }
//                int mod = 0;
//                for(int i = 0; i < len; i++){
//                    remainder[remainIndex++] = data[i];
//                    mod = mod +data[i];
//                }
//                remainder[remainIndex++] = (byte)mod;
//            }else{//回复没有文件内容了
//
//            }
//            int segment = 0;
//            int index = remainIndex;
//            while(len > 0){
//                for(int i = 0; i < 4; i++){
//                    remainder[index++] = (byte)(segment >> (8 * i));
//                    confirm[i + 4] = (byte)(segment >> (8 * i));
//                }
//                remainder[index++] = 1; //后续标志
//                confirm[8] = 1;
//                int mod = 0;
//                for(int i = 0; i < len; i++){
//                    remainder[index++] = data[i];
//                    mod = mod +data[i];
//                }
//                mod = mod % 256;
//                remainder[index++] = (byte)mod;
//
//                len = bis.read(data);
//                if(len <= 0){
//                    remainder[8] = 0;
//                    confirm[8] = 8;
//                }
//                lstByteFiles.add(Arrays.copyOf(remainder,index));
////                lstByteFiles.add(confirm);
//                index = remainIndex;
//                segment += len;
//            }
//        }catch (IOException e){
//            e.printStackTrace();
//        }
        return lstByteFiles;
    }

    private int singleFileProcess(byte[] source,byte[] fileArray,AttachFile af,int beginIndex){
        source[beginIndex++] = (byte)fileArray.length;
        for(byte b : fileArray){
            source[beginIndex++] = b;
        }
        source[beginIndex++] = (byte)af.getAttribute();
        int fileSize = af.getSize();
        for(int i = 0; i < 4; i++){
            source[beginIndex++] = (byte)(fileSize>>(8*i));
        }
        byte[] fileTime = af.getCp56time();
        for(byte b : fileTime){
            source[beginIndex++] = b;
        }
        return beginIndex;
    }

    private void copyDataToArray(final byte[] remainder,final byte[] fileInfos,final int remainIndex,final int idx,final List<byte[]> fileData){
        byte[] newArray = new byte[remainIndex + idx];
        System.arraycopy(remainder, 0, newArray, 0, remainIndex);
        System.arraycopy(fileInfos, 0, newArray, remainIndex, idx);
        fileData.add(newArray);
    }
}
